home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
TeX 1995 July
/
TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO
/
dviware
/
ivd2dvi
/
ivd2dvi.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-10-01
|
33KB
|
958 lines
/*
* ivd2dvi.
* Copyright 1988 by Larry Denenberg. May be freely distributed as long
* as this notice is retained.
*
* This is a general discussion of ivd2dvi, assuming familiarity with
* the format of DVI files and with the semantics of the reflection
* commands (see Knuth and Mackay, "Mixing right-to-left text with
* left-to-right text" in TUGboat volume 8 number 1). Notation: let
* "BR" stand for the BEGIN_REFLECT command, let "ER" stand for
* END_REFLECT, let "a" stand for SET_097 (i.e. "typeset character 97,"
* usually lowercase a), and similarly for the other lowercase letters.
*
* The simplest idea would be to rearrange reflected text somehow so
* that the right thing happens. Is it enough to reverse the commands?
* Certainly if we see "BR a b c ER" we can replace it with "c b a".
* But this works only so long as the reflected segment sticks to a
* small subset of DVI commands; "BR a FONT_00 b ER" can't in general be
* changed to "b FONT_00 a". We also get in trouble with commands that
* change the DVI registers (not to mention PUSH and POP!).
*
* Some of these difficulties could be overcome with more clever
* rearrangement. Unfortunately, there are situations that can't be
* handled by any such scheme: for example, you can't typeset the
* sequence "BR PUSH i POP w ER" without knowing about the widths of
* "i" and "w". The PUTn commands also lead to unsolvable problems.
* So we abandon this approach and resign ourselves to reading width
* data from TFM files.
*
* Here's the method we use: we read through the input DVI file,
* copying DVI commands to the output file. When we see a BR we stop
* copying and simply read the input looking for the matching ER.
* During this scan we keep track of the total width of the commands
* between the BR and ER. (Each character contributes its width as
* revealed by the TFM file, horizontal motions contribute their
* width, vertical motions are ignored, etc.) This procedure is
* called *simulating*; at its end we've just read an ER and we know
* the width of the segment between the BR and the ER. Call this
* width T. Note that nothing at all is output during simulation.
*
* Now we move right (in the output file) by T; of course we do so by
* shipping out a RIGHTn command. Next, we make a second pass over the
* commands between the BR and the ER; this time, we typeset them
* "backwards." For example, to SET an "a" we first move left by the
* width of an "a", and lay down the "a" without moving---this is the
* inverse of laying down the "a" and then moving right. If the command
* is any sort of horizontal motion we negate the distance before moving.
* Reflection applies only to horizontal motion, however; vertical
* motion commands, font changes, font definitions, and so forth, are
* copied unchanged. (For the precise way in which we reverse, see the
* documentation for the individual DVI commands below, especially
* /SetChar/ and /SetString/.)
*
* When we see the ER for the second time, the horizontal location in
* the output file should be the same as it was when we first saw the BR
* (since we've processed all the same commands, only backwards). So we
* once again output a command to move right by T, thus moving to the
* place where we typeset the first command after the BR. We're now
* done translating this reflected segment and we continue normally.
*
* What happens when reflections are nested? Well, it's easy. If we
* encounter a BR during a simulation, we basically ignore it! We're
* trying to find the total width of the reflected segment; the fact
* that part of the segment is further reflected doesn't matter. (We do
* have to be certain to skip over the inner ER so that we match only
* the ER we're looking for.) Later, when we're no longer simulating
* but "typesetting backwards," we'll encounter the BR of the inner
* reflection. At that point it gets its own simulation set up, and we
* do the whole two-pass business again on the inner segment; of course,
* the second pass is now a "forwards" pass, at the end of which we
* continue the "backwards" pass over the outer segment.
*
* All of this can be nested to any depth. We keep track of what we're
* doing with the variable /State/ which takes on the following values:
* LTYPESETTING Typesetting normally, left-to-right
* RTYPESETTING Typesetting "backwards," right-to-left
* SIMULATING First pass over text to be reflected
* >SIMULATING Inside nested begin_reflect/end_reflect pairs
* Values of /State/ greater than SIMULATING are used to keep track of
* the nesting depth of BR/ER pairs. While we're simulating, we simply
* increment /State/ when we see BR and decrement it when we see ER.
* We switch from simulating to typesetting only when we see ER while
* /State/ is exactly equal to SIMULATING. Note carefully that we're
* never simulating on behalf of more than one BR/ER pair; there's never
* more than one segment whose width we're measuring.
*
* So the procedure upon hitting a BR in the general case is as follows:
* if /State/ equals or exceeds SIMULATING, just increment /State/.
* Otherwise set /State/ to SIMULATING and start the first pass. The
* actions necessary at ER are a bit more complex: if /State/ is
* *greater* than SIMULATING, just decrement it. If /State/ is equal to
* SIMULATING, we've just finished a simulation: output the motion
* command as above, switch to the direction opposite to the one we were
* in when we hit the BR, and reset the input file to the point just
* after the BR, thus beginning the second pass. If /State/ is
* LTYPESETTING or RTYPESETTING, we've just finished the second pass
* over a reflection: invert /State/, and output the second long motion
* command as described above. Of course, there's lots of things to
* save and restore here; details are in the code below.
*
* Even when we're not simulating we have to keep track of certain
* values. The current font is an obvious example: we have to remember
* what it is, because if we start a simulation we'll have to measure
* its characters. We also keep track of the horizontal motion
* parameters: if a W0 appears in reflected text we must know the
* current value of W, which may have been set before the simulation
* began. Therefore we keep variables in which we store the values, and
* a stack to model all pushes and pops in the input file. The same
* stack is used to store values over a simulation; no conflict can
* occur. On the other hand, the values of the vertical motion
* parameters never concern us.
*
* One final twist. Suppose we encounter the following sequence: "BR
* <set W to d> a W0 b W0 c W0 ER" Now if we weren't clever, each W0
* would have to be replaced by a longer RIGHTn command, because when
* we're typesetting backwards a W0 translates to a rightward motion by
* -d, not d. It's better to set W to -d to begin with; then we can use
* W0 inside the reflection and save considerable space. So we negate
* the parameter of Wn commands encountered while RTYPESETTING. But as
* a consequence, the input file may disagree with the output file over
* the current value of W. So we must keep two separate variables,
* /WInput/ and /WOutput/, that record the two values. These two
* variables always are equal in absolute value, but we can't compute
* from the state whether they're equal or not (because reflections may
* start and end independently of changes to W). More details are given
* at /SetWandMoveForward/. All of this applies to X as well. With
* more work we could do further optimization, catching (e.g.) cases
* like "<set W to d> BR a W0 b W0 ER" in which W0 will not be used as
* our scheme stands.
*
* Jacques Goldberg first suggested the possibility of a dvi-ivd to dvi
* processor. Please send comments, suggestions, and bug reports to
* larry@bbn.com or larry@harvard.edu.
*
*/
#include <stdio.h>
#include "global.h"
#include "commands.h"
/* Global variables and procedures defined in auxiliary.c. */
extern font *CurFont, *FindFont();
extern void Initializations(), BadDVIAbort(), MaxPushLevelOutput();
extern void PushWrite(), PopWrite();
extern void PushWX(), PopXW(), PushDeltaH(), PopDeltaH();
extern void FontDefine(), CopyFontDefinition();
/* Global variables and procedures defined in io.c. */
extern unsigned BufSize, SignedBytes();
extern long BytesOutput, CopyWord();
extern long ReadSigned(), ReadUnsigned(), CharWidth(), CopyUnsigned();
extern void WriteByte(), WriteString(), WriteNumber(), WriteWord();
extern void CopyNBytes(), SkipNBytes();
extern void RereadLastByte(), ResetFilePosition();
extern unsigned_byte CopyByte(), ReadByte(), ReadCommand();
extern unsigned_byte *ReadFilePosition();
/* Global variables defined here */
char *ProgramName; /* argv[0], used in error messages */
boolean VerboseOutput = FALSE; /* v flag: report page progress */
boolean ExactOutput = FALSE; /* X flag: try not to change input file */
long PrevPagePointer = -1; /* output file location of previous BOP */
int State; /* current direction or simulation depth*/
long WInput, WOutput; /* value of W in input and output files */
long XInput, XOutput; /* value of X in input and output files */
long DeltaH; /* total size of reflected segment */
int SavedState; /* state at start of current simulation */
font *SavedFont; /* font at start of current simulation */
unsigned_byte *SavedPosition; /* file position at start of simulation */
unsigned_byte CurCommand; /* DVI command under consideration */
#define REVERSE(STATE) (LTYPESETTING + RTYPESETTING - STATE)
/* Procedures defined in this file, in order of definition */
void main(),Arguments(),FileArgument(),Preliminaries(),MainLoop();
void SetChar(),SetRule(),SetString(),SetFont();
void BeginPage(),EndPage(),BegReflect(),EndReflect();
void MoveForward(),SetWandMoveForward(),SetXandMoveForward();
void Postliminaries(),CopyParametrizedCommand();
/*
* Main procedure, whose function is self-documenting. The only way
* ivd2dvi can terminate normally is through the /exit/ here.
*/
void
main(ignore, argv)
int ignore;
char *argv[];
{
Arguments(argv);
Initializations();
Preliminaries();
MainLoop();
Postliminaries();
exit(0);
}
/*
* Standard argument processing. Don't worry if a flag is given more
* than once, but allow at most one filename. We also allow forms like
* "-Xv", or even "-Xbv 1024" since the argument following any -b is
* taken as the new buffer size.
*/
void
Arguments(argv)
char *argv[];
{
char *arg;
boolean seenfile = FALSE;
int newbufsize;
ProgramName = argv[0];
for (arg = *++argv; arg; arg = *++argv)
if (*arg == '-')
while (*++arg)
switch(*arg) {
case 'X':
ExactOutput = TRUE;
break;
case 'v':
VerboseOutput = TRUE;
break;
case 'b':
if (!*++argv) {
fprintf(stderr, "%s: missing buffer size, -b ignored\n",
ProgramName);
return;
} else {
newbufsize = atoi(*argv);
if (newbufsize == 0)
fprintf(stderr, "%s: illegal buffer size %s ignored\n",
ProgramName, *argv);
else
BufSize = newbufsize;
}
break;
default:
fprintf(stderr, "%s: illegal flag %c ignored\n",
ProgramName, *arg);
}
else if (seenfile)
fprintf(stderr, "%s: superflous filename %s ignored\n",
ProgramName, arg);
else {
seenfile = TRUE;
FileArgument(arg);
}
}
/*
* Process a file argument. Try to open the file. If we can't, and if
* the name has no period after its rightmost slash, append ".dvi" and
* try again. In either case we're reopening standard input, so that
* we can read the input DVI file from /stdin/ whether or not there's a
* filename on the command line.
*/
void
FileArgument(filename)
char *filename;
{
char buf[MAXFILENAMESIZE], *p;
if (freopen(filename, "r", stdin) != NULL) return;
p = rindex(filename, '/');
if (*p == '\0') p = filename;
p = index(p, '.');
if (*p != '\0') {
fprintf(stderr, "%s: Can't open %s\n", ProgramName, filename);
exit(1);
} else {
(void) sprintf(buf, "%s.dvi", filename);
if (freopen(buf, "r", stdin) != NULL) return;
fprintf(stderr, "%s: Can't open %s nor %s\n",
ProgramName, filename, buf);
exit(1);
}
}
/*
* Process the preamble. We mostly just copy it through untouched,
* except that we check the first two bytes for correctness and update
* the comment string to say ivd2dvi was here. (Don't update the
* comment string if the -X flag was used nor when the new comment
* wouldn't fit.)
*/
void
Preliminaries()
{
static char *comment = "; postprocessed by ivd2dvi";
unsigned comlength;
if (CopyByte() != PRE) BadDVIAbort("no preamble");
if (CopyByte() != DVIVERSION)
BadDVIAbort("wrong DVI version in preamble");
CopyNBytes(12L);
comlength = ReadByte();
if (!ExactOutput && (comlength < 256 - strlen(comment))) {
WriteByte(comlength + strlen(comment));
CopyNBytes((long) comlength);
WriteString(comment);
} else {
WriteByte(comlength);
CopyNBytes((long) comlength);
}
}
/*
* The main loop. Read a command, switch on it, and continue doing so
* forever. The only way out of this routine is when /CurCommand/ is
* POST. Most cases of the main switch are simply procedure calls; the
* rest are documented case-by-case. We first handle the most common
* case, for top speed: if /CurCommand/ is SET_000 through SET_127 and
* we're typesetting normally, we need do nothing but write it into the
* output. If we're simulating or typesetting backwards, /SetString/
* will handle it. Other commands are handled inside the switch. Note:
* the first test relies on the assumption that SET_000 is 0, which we
* really shouldn't do, but then again it *is* the test for the most
* common case! Expressions like "CurCommand - W1 + 1" return the
* number of bytes in the first parameter of commands with four forms.
*/
#define SETTING TRUE
void
MainLoop()
{
while (TRUE) {
CurCommand = ReadCommand();
if (CurCommand <= SETC_127) {
if (State == LTYPESETTING) WriteByte(CurCommand); else SetString();
continue;
}
switch (CurCommand) {
case SET1: case SET2: case SET3: case SET4:
SetChar(CurCommand - SET1 + 1, SETTING);
break;
case PUT1: case PUT2: case PUT3: case PUT4:
SetChar(CurCommand - PUT1 + 1, !SETTING);
break;
case SET_RULE: case PUT_RULE:
SetRule();
break;
case NOP:
if (ExactOutput) WriteByte(NOP);
break;
case BOP:
BeginPage();
break;
case EOP:
EndPage();
break;
/*
* When we see a PUSH/POP we save/restore the horizontal parameters.
* If we're simulating, we also have to save/restore /DeltaH/, since
* stuff that happens between the PUSH and the POP has no effect on
* the size of the reflected segment. (If we're not simulating, we
* don't care at all about /DeltaH/ and there's no sense saving it.)
* Note that the stack discipline works out as long as we restore
* in the opposite order that we save, since PUSH/POP must nest with
* respect to reflection---so we can't have a PUSH while simulating
* that matches a POP while not simulating. Oh yeah, I almost forgot:
* if we're *not* simulating, we must write the command to the output!
*/
case PUSH:
PushWX();
if (State < SIMULATING) PushWrite(); else PushDeltaH();
break;
case POP:
if (State < SIMULATING) PopWrite(); else PopDeltaH();
PopXW();
break;
case RIGHT1: case RIGHT2: case RIGHT3: case RIGHT4:
MoveForward(ReadSigned(CurCommand - RIGHT1 + 1));
break;
/*
* Move forward by W. It's up to /MoveForward/ whether "forward" means
* left or right (or neither, if we're just simulating). The amount to
* move is of course the *input* file's idea of the current W.
*/
case W0:
MoveForward(WInput);
break;
case W1: case W2: case W3: case W4:
SetWandMoveForward(ReadSigned(CurCommand - W1 + 1));
break;
/*
* Move forward by X. Please see the comments for W0, above.
*/
case X0:
MoveForward(XInput);
break;
case X1: case X2: case X3: case X4:
SetXandMoveForward(ReadSigned(CurCommand - X1 + 1));
break;
/*
* We don't especially care about vertical motion, so if we're only
* simulating there's nothing to do. Otherwise, copy the command and
* its parameters to the output file.
*/
case DOWN1: case DOWN2: case DOWN3: case DOWN4:
if (State < SIMULATING)
CopyParametrizedCommand(CurCommand - DOWN1 + 1);
break;
case Y0: case Y1: case Y2: case Y3: case Y4:
if (State < SIMULATING) CopyParametrizedCommand(CurCommand - Y0);
break;
case Z0: case Z1: case Z2: case Z3: case Z4:
if (State < SIMULATING) CopyParametrizedCommand(CurCommand - Z0);
break;
/*
* We must keep track of all font changes, simulating or not. So call
* /SetFont/ on the selected font number, and if we're not simulating
* copy the whole command to the output file.
*/
case FNT1: case FNT2: case FNT3: case FNT4:
if (State >= SIMULATING)
SetFont(ReadUnsigned(CurCommand - FNT1 + 1));
else {
WriteByte(CurCommand);
SetFont(CopyUnsigned(CurCommand - FNT1 + 1));
}
break;
/*
* Skip past if simulating, otherwise copy out the whole command.
*/
case XXX1: case XXX2: case XXX3: case XXX4:
if (State >= SIMULATING)
SkipNBytes(ReadUnsigned(CurCommand - XXX1 + 1));
else {
WriteByte(CurCommand);
CopyNBytes(CopyUnsigned(CurCommand - XXX1 + 1));
}
break;
case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4:
FontDefine(CurCommand - FNT_DEF1 + 1);
break;
case PRE:
BadDVIAbort("unexpected PRE");
break;
/*
* Shouldn't see the postamble while simulating or reversing; otherwise
* it's time to quit. This is the only normal way out of /MainLoop/.
*/
case POST:
if (State != LTYPESETTING) BadDVIAbort("unexpected POST");
return;
case POST_POST:
BadDVIAbort("unexpected POST_POST");
break;
case BEG_REFLECT:
BegReflect();
break;
case END_REFLECT:
EndReflect();
break;
/*
* The only commands left, besides illegal ones, are the one-byte font
* selection commands. Copy them to the output file unless simulating.
* Then call /SetFont/ to note the change of font.
*/
default:
if (CurCommand >= FONT_00 && CurCommand <= FONT_63) {
if (State < SIMULATING) WriteByte(CurCommand);
SetFont((long) CurCommand - FONT_00);
} else
BadDVIAbort("unrecognized command");
} /* end of "switch (CurCommand) { ... }" */
} /* end of "while (TRUE) { ... }" */
} /* end of procedure MainLoop */
/*
* Set a single character as specified by a SETn or PUTn command.
* (SET_nnn commands don't come here; they go to /SetString/.)
* /setting/ tells whether it was a SET or a PUT, and /bytes/ gives the
* parameter length. If we're typesetting normally we just copy out the
* command and parameter, and if we're simulating it's enough to add the
* width of the desired character to the running total. If we're
* typesetting in reverse there's more to do: first move "forward"
* (left, in this case) by the width of the character. Only then do we
* typeset the character. Moreover, if the original command was a SET,
* we PUT the character since we've already moved. And if the original
* command was a PUT, we SET the character---which has the effect of
* moving back to the right, for a net motion of zero.
*/
void
SetChar(bytes, setting)
unsigned bytes;
boolean setting;
{
long charnumber;
switch (State) {
case LTYPESETTING:
CopyParametrizedCommand(bytes);
break;
case RTYPESETTING:
charnumber = ReadUnsigned(bytes);
MoveForward(CharWidth(charnumber));
if (setting)
WriteByte(PUT1 + bytes - 1);
else
WriteByte(SET1 + bytes - 1);
WriteNumber(charnumber, bytes);
break;
default: /* State >= SIMULATING */
DeltaH += CharWidth(ReadUnsigned(bytes));
}
}
/*
* Same idea as /SetChar/, just above. If LTYPESETTING just copy, if
* simulating just accumulate the width. If RTYPESETTING first move
* left and then interchange SET and PUT.
*/
void
SetRule()
{
long height, width;
switch (State) {
case LTYPESETTING:
CopyParametrizedCommand(8);
break;
case RTYPESETTING:
height = ReadSigned(4);
width = ReadSigned(4);
MoveForward(width);
if (CurCommand == SET_RULE) WriteByte(PUT_RULE);
else WriteByte(SET_RULE);
WriteWord(height);
WriteWord(width);
break;
default: /* State >= SIMULATING */
SkipNBytes(4L);
width = ReadSigned(4);
if (CurCommand == SET_RULE) DeltaH += width;
}
}
/*
* Handle a SET_nnn command. This routine is called only while
* simulating or typesetting in reverse (the normal case is handled
* directly in /MainLoop/ for speed).
*
* If we're just simulating there's nothing to do except add the width
* of the character to the running total. The RTYPESETTING case is the
* interesting one. Here's the story: We could typeset backwards
* as we do in /SetChar/ above: move left, then PUT (since all these
* commands are variants of SET). But that would mean that every one
* of these common one-byte commands would translate into around five
* bytes. So instead of handling a single command, this routine handles
* /CurCommand/ and all subsequent SET_nnn commands at once. We first
* continue to read the input until we hit a command that is *not* a
* SET_nnn. As we do so, we accumulate the total width (call it T) of
* all the SET_nnn commands that we've read. Finally, we write the
* following instructions into the output file: (a) move forward---that
* is, left---by T, (b) PUSH, to save the new value of H, (c) typeset
* all the characters we've seen *backwards*, (d) POP to get back to
* the place where we started emitting characters. This does the trick.
*
* A few notes: We must back up the input after we're done since that
* first non-SET_nnn command must be reconsidered by /MainLoop/. We
* rely on the fact that /ReadFilePosition/ gives us a pointer into a
* buffer from which we can rattle off /count/ characters; this is true
* since we must be rescanning input after a simulation. Finally, if
* there's only a single SET_nnn character to typeset we save a byte by
* replacing steps (b), (c) and (d) with a simple PUT1.
*
* This routine relies on the fact that SET_nnn in fact has value nnn,
* which, stylistically, is absolutely indefensible.
*/
void
SetString()
{
unsigned_byte *p;
unsigned_byte nextcommand;
long totalwidth;
int count;
if (State >= SIMULATING)
DeltaH += CharWidth((long) CurCommand);
else { /* State == RTYPESETTING */
totalwidth = count = 0;
nextcommand = CurCommand;
do {
count++;
totalwidth += CharWidth((long) nextcommand);
nextcommand = ReadByte();
} while (nextcommand <= SETC_127);
RereadLastByte();
MoveForward(totalwidth);
if (count == 1) {
WriteByte(PUT1);
WriteByte(CurCommand);
} else {
PushWrite();
p = ReadFilePosition();
while (count--) WriteByte(*--p);
PopWrite();
}
}
}
/*
* Record the current font number, which must be done no matter what
* state we're in. The work is done by /FindFont/; we just put the
* result into /CurFont/ and complain if the font is unknown.
*/
void
SetFont(fontnumber)
long fontnumber;
{
CurFont = FindFont(fontnumber);
if (CurFont == NULL) BadDVIAbort("font used but not defined");
}
/*
* Pages should end only in normal left-to-right mode. We write the
* BOP, copy the ten \count registers printing the first if in verbose
* mode, and write out the pointer to the previous page. (We can't copy
* the old one since page lengths may change.) We also update this
* pointer; it must point to the newly written BOP. Finally, we start
* out the font, W, and X registers correctly.
*/
void
BeginPage()
{
long pagenumber;
if (State != LTYPESETTING) BadDVIAbort("unexpected BOP");
WriteByte(BOP);
pagenumber = CopyWord();
if (VerboseOutput) fprintf(stderr, "[%ld", pagenumber);
CopyNBytes(36L);
SkipNBytes(4L);
WriteWord(PrevPagePointer);
PrevPagePointer = BytesOutput - 45;
WInput = WOutput = XInput = XOutput = 0;
CurFont = NULL;
}
/*
* End a page. Must happen in "normal" LTYPESETTING mode, but otherwise
* there's nothing to do but write the EOP and chatter if requested.
*/
void
EndPage()
{
static int pages = 0;
if (State != LTYPESETTING) BadDVIAbort("unexpected EOP");
WriteByte(EOP);
if (VerboseOutput) fprintf(stderr, "]%c", (++pages % 10) ? ' ' : '\n');
}
/*
* Start of a reflected segment, which may happen in any state. If
* we're already simulating just increment /State/ to indicate nested
* reflections (see the overview above). Otherwise start simulating:
* save the horizontal motion parameters, the current file location, the
* current font, and the current state. Start the running total of the
* size of this segment at zero. Change state to SIMULATING to start
* the first pass over the segment; the rest of the work is done at
* /EndReflect/. One subtlety: it's OK to save the font, the state, and
* the file position in variables since there's never more than one
* simulation at a time. We could do the same with the horizontal
* motion parameters, but we might as well use the extant stack.
*/
void
BegReflect()
{
if (State >= SIMULATING) ++State;
else {
PushWX();
SavedPosition = ReadFilePosition();
SavedFont = CurFont;
SavedState = State;
State = SIMULATING;
DeltaH = 0;
}
}
/*
* Come here at the end of a reflected segment. We first comment the
* easy case, which happens to be coded last: If /State/ is greater
* than SIMULATING we're inside nested reflections that don't concern
* us yet, so just decrement /State/ to show that we're up a level.
*
* If /State/ says we're at the outermost simulation, we must wrap up
* the simulation and start the second pass. The direction will be the
* inverse of the direction just before the simulation started. We
* also reset the font and the horizontal parameters to their saved
* values, and set things up so we're rescanning the input just *after*
* the BEG_REFLECT that started the simulation. Finally, we move
* "forward" by the total width of the reflected segment; we'll now
* typeset everything "backward" from that point. (Note that we move
* "forward" by the *negative* of /DeltaH/; this is because we've
* already turned the state around. Exercise: why must the /PopXW/
* precede the /MoveForward/?) But we're not quite done with /DeltaH/;
* we need it at the *end* of the second pass to move forward over
* the reflected segment again. Since the variable /DeltaH/ is used
* by any nested simulations, we must save its value on the stack.
*
* If /State/ says we're typesetting (either direction) then we've
* just finished the second pass over a reflected segment: grab the
* saved value of /DeltaH/, use it to move back over the reflected
* segment, then revert to the previous direction of typesetting
* (which is the opposite of the direction just finished). Be sure
* to see the overview if you don't understand this move by /DeltaH/.
*
* Harder exercise: When we've finished typesetting a simulation as
* just described, we repeat the move that we started with to get back
* to the right place to continue. Why don't we use PUSH and POP *in
* the output file* to remember the place? That is, why not add a call
* to /PushWrite/ just after /MoveForward(-DeltaH)/ in the SIMULATING
* case, and *replace* the /MoveForward/ in the typesetting cases with
* a call to /PopWrite/? We'd save a couple of bytes in the output,
* and wouldn't have to worry about the value of /DeltaH/ during the
* second pass (thus we could skip pushing and popping /DeltaH/).
* What's the disadvantage that vitiates this clever idea?
*/
void
EndReflect()
{
switch(State) {
case SIMULATING:
CurFont = SavedFont;
ResetFilePosition(SavedPosition);
State = REVERSE(SavedState); /* must precede MoveForward */
PopXW(); /* must precede MoveForward */
MoveForward(-DeltaH);
PushDeltaH();
break;
case LTYPESETTING: case RTYPESETTING:
PopDeltaH();
MoveForward(-DeltaH);
State = REVERSE(State);
break;
default: /* State >SIMULATING */
State--;
}
}
/*
* Move "forward" by /distance/. If we're simulating it's enough to
* accumulate /distance/ into the running total, otherwise we need a
* motion command in the output file. Now "forward" means "right" if
* typesetting normally, but "left" if typesetting right-to-left, so
* first we negate distance if we're in the latter mode. If we can
* accomplish this move with a simple W0 or X0 we do so. Otherwise,
* we use a RIGHTn with the smallest possible n. Notice that we
* test /distance/ against the *output* file's idea of W and X when
* trying for the short versions. These may not agree with W and X
* in the input file; see the two routines immediately following.
*/
void
MoveForward(distance)
long distance;
{
unsigned bytes;
if (State >= SIMULATING) { DeltaH += distance; return; }
if (State == RTYPESETTING) distance = -distance;
if (distance == WOutput) WriteByte(W0);
else if (distance == XOutput) WriteByte(X0);
else {
bytes = SignedBytes(distance);
WriteByte(RIGHT1 - 1 + bytes);
WriteNumber(distance, bytes);
}
}
/*
* Process a command in the range W1..W4. The parameter has been
* already been read. We always need to set this distance into WInput,
* the input file's idea of W. If we're just simulating, just
* accumulate /distance/ into the current total and quit (the value of
* WOutput is irrelevant during simulation). If typesetting left-to-
* right, output a Wn command to move and set W in the output file. If
* typesetting right-to-left, the same, except we first negate
* /distance/. This means two things: first of all, we'll move the
* opposite direction in the output file as desired. Secondly, the
* value of W in the output file (recorded in /WOutput/) is the negative
* of its value in the input file. That way, upcoming W0 commands will
* be directly usable as long as we're typesetting backwards.
*/
void
SetWandMoveForward(distance)
long distance;
{
unsigned bytes;
WInput = WOutput = distance;
if (State >= SIMULATING) { DeltaH += distance; return; }
if (State == RTYPESETTING) WOutput = distance = -distance;
bytes = SignedBytes(distance);
WriteByte(W1 - 1 + bytes);
WriteNumber(distance, bytes);
}
/*
* Don't even think of finding comments for this routine. Just look up
* at /SetWandMoveForward/, OK?
*/
void
SetXandMoveForward(distance)
long distance;
{
unsigned bytes;
XInput = XOutput = distance;
if (State >= SIMULATING) { DeltaH += distance; return; }
if (State == RTYPESETTING) XOutput = distance = -distance;
bytes = SignedBytes(distance);
WriteByte(X1 - 1 + bytes);
WriteNumber(distance, bytes);
}
/*
* Process the postamble, having just read a POST command from the input
* file. Mostly we just copy the postamble into the output file, with a
* few exceptions: The previous page pointers and maximum stack depth may
* be different, and NOP commands are suppressed unless the -X flag was
* used. When we hit POST_POST, we finish off the file with four 223's
* and enough more so that the total file length is divisible by four.
* Note that ivd2dvi stops reading its input file after seeing the dvi
* version character here. As a consequence, it's always an error for
* the input file to come to EOF.
*/
void
Postliminaries()
{
long i;
if (VerboseOutput) fprintf(stderr, "[post]\n");
WriteByte(POST);
SkipNBytes(4L);
WriteWord(PrevPagePointer);
PrevPagePointer = BytesOutput - 5;
CopyNBytes(20L);
MaxPushLevelOutput();
CopyNBytes(2L);
while (CurCommand != POST_POST) {
CurCommand = ReadByte();
switch (CurCommand) {
case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4:
CopyFontDefinition(ReadSigned(CurCommand - FNT_DEF1 + 1),
CurCommand - FNT_DEF1 + 1);
break;
case NOP:
if (ExactOutput) WriteByte(CurCommand);
break;
case POST_POST:
break;
default:
BadDVIAbort("unrecognized command in postamble");
}
}
WriteByte(POST_POST);
SkipNBytes(4L);
WriteWord(PrevPagePointer);
if (CopyByte() != DVIVERSION)
BadDVIAbort("wrong DVI version in postamble");
for (i = 7 - ((BytesOutput-1) % 4); i > 0; i--) WriteByte(DVIPADCHAR);
}
/*
* Output the current command, then copy /bytes/ more bytes from the
* input file to the output file. Surprisingly useful.
*/
void
CopyParametrizedCommand(bytes)
unsigned bytes;
{
WriteByte(CurCommand);
CopyNBytes((long) bytes);
}